package esercitazione3.tda_RBTree;

import esercitazione3.exceptions.InvalidPositionException;
import esercitazione3.tda_BinaryTree.BTNode;
import esercitazione3.tda_BinaryTree.BTPosition;

 
/**
 * 
 * Classe che definisce il nodo generico di un albero RB.
 * Il nodo contiene estende un nodo BT dipendente da un oggetto generico E.
 * Il nodo RB necessita di un generico oggetto E che sia comparabile.
 *
 */
public class RBNode<E extends Comparable<E>> extends BTNode<E> {

	protected Color color;

	/**
	 * Costruttore di default del nodo dell'albero. Di default il nodo è nero.
	 */
	RBNode() {
		super();
		color = Color.BLACK;
	}

	 
	/**
	 * Costruttore del nodo dell'albero. Questo è il costruttore usato dagli RBTree poichè permette di
	 * impostare anche i valori relativi ai "vicini" del nodo (padre, fratelli).
	 * Per non modificare l'altezza nera dell'albero, il nodo di default è rosso.
	 * 
	 * @param element Elemento da inserire
	 * @param parent Padre del nodo che si sta creando
	 * @param left	Figlio sinistro
	 * @param right Figlio destro
	 */
	RBNode(E element, BTPosition<E> parent, BTPosition<E> left,
			BTPosition<E> right) {
		super(element, parent, left, right);
		color = Color.RED;
	}

	
	/**
	 * Metodo per verificare se il colore del nodo sia rosso
	 * @return true se il colore è rosso, altrimenti false
	 */
	public boolean isRed() {
		return color == Color.RED;
	}

	/**
	 * Metodo per impostare il colore a rosso
	 */
	public void makeRed() {
		color = Color.RED;
	}


	/**
	 * Metodo per impostare il colore a nero
	 */
	public void makeBlack() {
		color = Color.BLACK;
	}

	/**
	 * Metodo generico per impostare il colore
	 * @param color colore da impostare
	 */
	public void setColor(Color color) {
		this.color = color;
	}

	/**
	 * Metodo per ottenere il riferimento al padre del nodo
	 */
	public RBNode<E> getParent() {
		// è necessario un cast poichè usiamo un metodo degli alberi binari.
		return (RBNode<E>) super.getParent();
	}

	
	/**
	 * Metodo per ottenere il riferimento al figlio sinistro del nodo
	 */
	public RBNode<E> getLeft() {
		// è necessario un cast poichè usiamo un metodo degli alberi binari.
		return (RBNode<E>) super.getLeft();
	}

	/**
	 * Metodo per ottenere il riferimento al figlio destro del nodo
	 */
	public RBNode<E> getRight() {
		// è necessario un cast poichè usiamo un metodo degli alberi binari.
		return (RBNode<E>) super.getRight();
	}

	/**
	 * Metodo per ottenere il riferimento allo zio del nodo
	 * @throws InvalidPositionException
	 */
	public RBNode<E> getUncle() throws InvalidPositionException{
		// se il padre e il nonno sono null significa che non esiste lo zio
		if(this.getParent().element()!=null && this.getGrandfather()!=null)
			// se il nonno non ha due figli non esiste lo zio
			if(this.getGrandfather().getLeft().element()!=null || this.getGrandfather().getRight().element()!=null ){
				// se il nodo è il figlio sinistro, allora lo zio è il figlio destro
				if(this.getParent()==this.getGrandfather().getLeft())
					return this.getGrandfather().getRight();
				// altrimenti lo zio è il figlio sinistro
				else 
					return this.getGrandfather().getLeft();
			}
		// ritorna null se non esiste lo zio
		return null;
	}

	/**
	 * Metodo per ottenere il riferimento al nonno del nodo
	 * @return
	 * @throws ClassCastException
	 * @throws InvalidPositionException
	 */
	public RBNode<E> getGrandfather() throws ClassCastException, InvalidPositionException {
		// se esiste un padre (non è nodo radice) e se anche il padre ha a sua volta un padre (neanche il padre è radice)
		// allora il nodo ha un nonno e lo restituisco
		if (this.getParent() != null && this.getParent().getParent() != null)
			return (RBNode<E>) this.getParent().getParent();
		// altrimenti non ha un nonno e restiutisco null
		else
			return null;
	}
}
